package fr.mywiki.business.utils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Contains several methods dealing with String objects.
 * 
 * @author tlombard
 */
public class StringUtils {
	private StringUtils() {}

	private static final String PLAIN_ASCII = "AaEeIiOoUu" // grave
			+ "AaEeIiOoUuYy" // acute
			+ "AaEeIiOoUuYy" // circumflex
			+ "AaEeIiOoUuYy" // tilde
			+ "AaEeIiOoUuYy" // umlaut
			+ "Aa" // ring
			+ "Cc" // cedilla
	;

	private static final String UNICODE = "\u00C0\u00E0\u00C8\u00E8\u00CC\u00EC\u00D2\u00F2\u00D9\u00F9" // grave
			+ "\u00C1\u00E1\u00C9\u00E9\u00CD\u00ED\u00D3\u00F3\u00DA\u00FA\u00DD\u00FD" // acute
			+ "\u00C2\u00E2\u00CA\u00EA\u00CE\u00EE\u00D4\u00F4\u00DB\u00FB\u0176\u0177" // circumflex
			+ "\u00C2\u00E2\u00CA\u00EA\u00CE\u00EE\u00D4\u00F4\u00DB\u00FB\u0176\u0177" // tilde
			+ "\u00C4\u00E4\u00CB\u00EB\u00CF\u00EF\u00D6\u00F6\u00DC\u00FC\u0178\u00FF" // umlaut
			+ "\u00C5\u00E5" // ring
			+ "\u00C7\u00E7" // cedilla
	;

	// remove accentued from a string and replace with ascii equivalent
	public static String removeAccents(String s) {
		StringBuffer sb = new StringBuffer();
		int n = s.length();
		for (int i = 0; i < n; i++) {
			char c = s.charAt(i);
			int pos = UNICODE.indexOf(c);
			if (pos > -1) {
				sb.append(PLAIN_ASCII.charAt(pos));
			} else {
				sb.append(c);
			}
		}
		return sb.toString();
	}

	/**
	 * Substitutes every ponctuation sign (i.e. every character that is not a space, a letter of a
	 * figure) by a space character.
	 * 
	 * @param str a <code>String</code> object with or without ponctuation.
	 * @return a <code>String</code> object without ponctuation.
	 */
	public static String subPonctuationBySpace(String str) {
		char[] array = str.toCharArray();
		for (int i = 0; i < array.length; i++) {
			char achar = array[i];
			if (!(isLetter(achar) || isFigure(achar) || achar == ' '))
				array[i] = ' ';
		}
		return new String(array);
	}

	/**
	 * Is this character a letter ?
	 * 
	 * @param achar any character.
	 * @return <code>true</code> if the character is a letter, false otherwise.
	 */
	public static boolean isLetter(char achar) {
		return ((achar >= 'a' && achar <= 'z') || (achar >= 'A' && achar <= 'Z'));
	}

	/**
	 * Is this character a figure, i.e. 0, 1, 2,... or 9 ?
	 * 
	 * @param achar any character.
	 * @return <code>true</code> if the character is a figure, false otherwise.
	 */
	public static boolean isFigure(char achar) {
		return (achar == '0' || (achar >= '1' && achar <= '9'));
	}

	/**
	 * This method courses along the string given in parameter and substitutes any set of following
	 * spaces by a single space.
	 * 
	 * @param str a <code>String</code> object with or without sequence of spaces.
	 * @return a <code>String</code> object without sequence of spaces longer than 2.
	 */
	public static String removeDuplicateSpaces(String str) {
		return str.replaceAll("[ ]+", " ");
	}

	/**
	 * <p>
	 * This method returns the intern form of a <code>String</code> object.
	 * The intern form of a string is a way to rewrite it in a easier-to-use
	 * format. Two <code>String</code> objects which are considered equivalent
	 * should have the same intern form. The processes to transform a string
	 * into an intern string is, in this order :
	 * </p>
	 * <p> - transform every letter in small letter.
	 * </p>
	 * <p> - remove accents.
	 * </p>
	 * <p> - substitute ponctuation signs with spaces.
	 * </p>
	 * <p> - substitute several following spaces with only one.
	 * </p>
	 * <p> - avoid spaces in first and last positions.
	 * </p>
	 * <p> - substitute every remaining space with a '_'.
	 * </p>
	 * 
	 * @param str the <code>String</code> object to be converted.
	 * @return a <code>String</code> object.
	 */
	public static String getInternForm(String str) {
		str = str.toLowerCase(); // To lower case
		str = removeAccents(str); // remove accents
		str = subPonctuationBySpace(str); // remove ponctuation
		str = removeDuplicateSpaces(str); // remove duplicate spaces
		str = str.trim(); // Remove begin and end spaces
		str = str.replaceAll("[ ]", "_");
		return str;
	}
	
	/**
	 * Slices the String str into several substrings, using a set of characters as
	 * delimiters. When slicing the string at a specific position, the delimitor
	 * which was found is includedin the right substring.
	 * <p>
	 * The string <tt>"^10|22^37|45|56^"</tt> and the delimiters
	 * <tt>{'^','|'}</tt>, for example, will return the following array:
	 * <tt>{"^10","|22","^37","|45","|56","^"}</tt>.
	 * 
	 * @param str
	 *          a <code>String</code> object to slice.
	 * @param delim
	 *          a set of delimiter characters.
	 * @return a List of String objects
	 * 
	 * @see String#split(java.lang.String)
	 */
	public static List sliceString(String str, String[] delim) {
		List result = new ArrayList();

		String curStr = str;

		while (curStr.length() > 1) {
			String curDelim = delim[indexOf(curStr, delim, 1)];
			int index = curStr.indexOf(curDelim, 1);

			if (index < Integer.MAX_VALUE) {
				String slice = curStr.substring(0, index);
				result.add(slice);
				curStr = curStr.substring(index);
			} else
				curStr = new String();
		}

		return result;
	}
	
	/**
   * Slices the incoming string argument into an array of String of size 1.
   * 
   * @param str
   *          a String object
   * @return a array of String (as Object)
   */
	public static Object[] toStringArray(String str) {
		char[] chArray = str.toCharArray();
		
		int length = chArray.length;
		Object[] objArray = new String[length];
		
		for(int i=0;i<length;i++)
			objArray[i] = Character.toString(chArray[i]);
		
		return objArray;
	}
	
	/**
	 * Computes the first index of appearance in str of a char contained in delim.
	 * The search begins at the from-th position.
	 * 
	 * @param str
	 *          the string to search in.
	 * @param delim
	 *          a set of char delimitators to search for.
	 * @param from
	 *          a position to search from it.
	 * @return an integer
	 */
	private static int indexOf(String str, String[] delim, int from) {
		int delimSize = delim.length;
		int[] indexes = new int[delimSize];
		
		for(int i=0;i<delimSize;i++) {
			indexes[i] = str.indexOf(delim[i],from);
		}
		
		return ArrayUtils.getMinimumPositive(indexes);
	}
	
	/**
	 * Returns true if the string given in parameter is null or empty (i.e. his length is 0)
	 * 
	 * @param str the string to test
	 * @return true if the string is null or empty
	 */
	public static boolean isNullOrEmpty(String str) {
		return (str == null || str.length() < 1);
	}
	
	/**
	 * Longest common substring between two strings
	 * 
	 * @param str1
	 *            a String
	 * @param str2
	 *            a String
	 * @return an array of 3 integers : the first one is the length of the
	 *         longest common substring, the secund is the position of the LCS
	 *         in str1, the third is the position of the LCS in str2.
	 */
	public static int[] longestCommonSubstring(String str1, String str2) {
		if (isNullOrEmpty(str1) || isNullOrEmpty(str2))
			return new int[] { 0, 0, 0 };

		int num[][] = new int[str1.length()][str2.length()];
		int maxlen = 0;
		int pos1 = -1;
		int pos2 = -1;

		for (int i = 0; i < str1.length(); i++) {
			for (int j = 0; j < str2.length(); j++) {
				if (str1.charAt(i) != str2.charAt(j))
					num[i][j] = 0;
				else {
					if ((i == 0) || (j == 0)) {
						num[i][j] = 1;
					}
					else
						num[i][j] = 1 + num[i - 1][j - 1];

					if (num[i][j] > maxlen) {
						maxlen = num[i][j];
						pos1 = i;
						pos2 = j;
					}
				}
			}
		}
		return new int[] { maxlen, pos1 - maxlen + 1, pos2 - maxlen + 1 };
	}
	
	/**
	 * Returns a String representing the list of elements, each one being returned
	 * as its String value, and all values separated by a comma.
	 * 
	 * @param elements
	 *          a <code>List</code> of elements, the toString() method being
	 *          used to display each of them.
	 * @return a <code>String</code> object
	 */
	public static String toString(List elements) {
		StringBuffer buffer = new StringBuffer();
		
		Iterator itElts = elements.iterator();
		while(itElts.hasNext()) {
			buffer.append(itElts.next().toString());
			if(itElts.hasNext())
				buffer.append(", ");
		}
		
		return buffer.toString();
	}
}
